package cn.com.cennavi.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.spark.api.java.function.Function;

import com.cennavi.mm.astar.Projection;
import com.cennavi.mm.service.RouteEstLocal;
import com.cennavi.mm.thrift.GPS;
import com.cennavi.mm.thrift.RouteEstimationLink;

import cn.com.cennavi.bean.DiDiNewData1;
import cn.com.cennavi.bean.DiDiTrajectory;
import cn.com.cennavi.bean.DidiQueue2;
import cn.com.cennavi.bean.DifPointTotalMapInfo;
import scala.Tuple2;

public class MapMatchFunction2 implements Function<Tuple2<String, DidiQueue2>, Tuple2<String, DiDiTrajectory>> {

	private static final long serialVersionUID = 1L;
	private static final String ERROR = "ERROR";

	@Override
	public Tuple2<String, DiDiTrajectory> call(Tuple2<String, DidiQueue2> arg0) throws Exception {
		// TODO Auto-generated method stub
		LinkedList<DiDiNewData1> enterBufQueue = arg0._2.enterBufQueue;
		HashMap<String, DifPointTotalMapInfo> difPoint_dir_bufferMap = arg0._2.difPoint_dir_bufferMap;
		String key = arg0._1;
		RouteEstLocal re = null;
		DiDiTrajectory didi = null;
		try {
			re = new RouteEstLocal("172.20.10.141:9001", new HashMap<String, String>(), "1");
			TreeMap<Long, GPS> gpsMap = new TreeMap<>();
			for (DiDiNewData1 odb : enterBufQueue) {
				GPS gps = new GPS(odb.lon, odb.lat, odb.direction, odb.timeStamp, (int) (odb.speed));
				gpsMap.put(gps.time, gps);
			}
			// 如果候选buffer不是唯一的，则不进行匹配了
			if (difPoint_dir_bufferMap.size() != 1) {
				return new Tuple2<String, DiDiTrajectory>(ERROR + ":" + ERROR, null);
			}
			if (gpsMap.size() < 2) {
				return new Tuple2<String, DiDiTrajectory>(ERROR + ":" + ERROR, null);
			}
			List<RouteEstimationLink> trace = re.getRoute(gpsMap, true);

			String linkseq;
			if (trace != null) {
				linkseq = judgeRoadIsDifPointRoad(trace, difPoint_dir_bufferMap);
				if (linkseq != null) {
					didi = DiDiTrajectory.getDiDiTrajectory(key, linkseq);
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			re.close();
		}
		if (didi != null) {
			return new Tuple2<String, DiDiTrajectory>(key, didi);
		} else {
			return new Tuple2<String, DiDiTrajectory>(ERROR + ":" + ERROR, null);
		}
	}

	/**
	 * 计算每条link距离最末尾link的距离
	 */
	private HashMap<String, Integer> getLinkToStartLenMap(DifPointTotalMapInfo totalMapInfo) {
		// TODO Auto-generated method stub
		HashMap<String, Integer> dataMap = new HashMap<>();
		String[] linksLen = totalMapInfo.linksLen.split("\\|");
		int len = linksLen.length;
		int length = 0;
		for (int i = len - 1; i >= 0; i--) {
			String[] linkId_len = linksLen[i].split("\\:");
			String linkId = linkId_len[0];
			length += Integer.parseInt(linkId_len[1]);
			dataMap.put(linkId, length);
		}
		return dataMap;
	}

	private String judgeRoadIsDifPointRoad(List<RouteEstimationLink> trace, HashMap<String, DifPointTotalMapInfo> difPoint_dir_bufferMap) {
		// 判断是不是有交集，如果有把相交的部分提取出来
		String links = "";
		// linkid按照时间排序，保存排好序的位置和速度
		HashMap<String, TreeMap<Long, String>> link_dis_speed_list = new HashMap<>();
		try {
			// 循环便利，得到每条link包含的投影点信息，和link序列
			for (int i = 0; i < trace.size(); i++) {

				ArrayList<Projection> proj = trace.get(i).projList;
				if (proj == null)
					continue;
				for (Projection projection : proj) {
					String linkId = projection.linkID;
					double dis = projection.prjDistanceFormSNode;
					long timeStamp = projection.gps.time;
					int Speed = projection.gps.speed;
					double len = projection.linkLenth;
					TreeMap<Long, String> linkTreeMap = link_dis_speed_list.get(linkId);
					if (linkTreeMap == null) {
						TreeMap<Long, String> linkTreeMap1 = new TreeMap<>();
						linkTreeMap1.put(timeStamp, dis + "_" + Speed + "_" + len);
						link_dis_speed_list.put(linkId, linkTreeMap1);
					} else {
						linkTreeMap.put(timeStamp, dis + "_" + Speed + "_" + len);
					}
				}
				links += trace.get(i).linkID;
				if (i != trace.size() - 1) {
					links += "|";
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		// 判断link序列与候选缓冲区link的交集
		for (Map.Entry<String, DifPointTotalMapInfo> entry1 : difPoint_dir_bufferMap.entrySet()) {
			DifPointTotalMapInfo data = entry1.getValue();
			int roadclass = data.roadClass;
			String[] inoutlink = data.enter_out_DifPoint_Link.split("\\|");
			// link序列至少包含进分歧点link和出分歧点link两条link
			boolean hasInOutLink = judgeHasInOutLink(links, inoutlink);
			if (!hasInOutLink)
				continue;
			String difPointId_dir = entry1.getKey();
			DifPointTotalMapInfo totalMapInfo = entry1.getValue();
			String linkSeq1 = totalMapInfo.linkArr;
			String[] geoLinks = linkSeq1.split("\\|");
			String linkeq = "", StartLink = "";
			int num = -1;
			for (int i = 0; i < geoLinks.length; i++) {
				if (links.contains(geoLinks[i])) {
					StartLink = geoLinks[i];
					linkeq = StartLink;
					num = i;
					break;
				}
			}
			if (num == -1) {
				continue;
			}
			/**
			 * 因为前面已经做过判断hasInOutLink，link数量肯定超过两条
			 */
			for (int j = num + 1; j < geoLinks.length; j++) {
				linkeq += "|";
				linkeq += geoLinks[j];
				if (links.contains(linkeq)) {
					StartLink = linkeq;
				} else {
					break;
				}
			}
			// 得到匹配出来的link序列，StartLink,根据StartLink顺序获取link的投影信息
			String[] linksArr = StartLink.split("\\|");
			StringBuffer timeSb = new StringBuffer();
			StringBuffer disSb = new StringBuffer();
			StringBuffer speedSb = new StringBuffer();
			HashMap<String, Integer> linkToStartLenMap = getLinkToStartLenMap(totalMapInfo);
			for (String link : linksArr) {
				TreeMap<Long, String> map = link_dis_speed_list.get(link);
				if (map != null)
					for (Map.Entry<Long, String> entry : map.entrySet()) {
						long time = entry.getKey();
						// 保证匹配点的顺序性
						if (timeSb.toString().contains(time + "")) {
							continue;
						}
						String[] dis_speed = entry.getValue().split("\\_");
						// 匹配到link端点出舍弃
						double diss = Double.parseDouble(dis_speed[0]);
						double lenLink = Double.parseDouble(dis_speed[2]);
						if (diss == 0 || diss == lenLink) {
							continue;
						}
						// 计算gps点距离最末尾link的距离
						int disFromEnd = (int) (linkToStartLenMap.get(link) - Double.parseDouble(dis_speed[0]));
						// 计算gps点距离同一方向分叉了的公共部分的最后一条link的距离
						int disFromMinSameLinkDis = disFromEnd - totalMapInfo.minSuitableDisFromLastLink;
						// 如果gps点落在同一方向（比如左转）但是有分叉的道路的公共部分，则保存，否则为无效数据
						if (disFromMinSameLinkDis >= 0 && disFromMinSameLinkDis <= totalMapInfo.samelinkLen) {
							timeSb.append(entry.getKey());
							timeSb.append("|");
							disSb.append(disFromMinSameLinkDis);
							disSb.append("|");
							speedSb.append(dis_speed[1]);
							speedSb.append("|");
						}
					}
			}
			StringBuffer sb = new StringBuffer();
			if (timeSb != null && !timeSb.equals("")) {
				sb.append(difPointId_dir);
				sb.append("*");
				sb.append(linkSeq1);
				sb.append("*");
				sb.append(StartLink);
				sb.append("*");
				sb.append(totalMapInfo.toString3());
				sb.append("*");
				sb.append(timeSb.toString());
				sb.append("*");
				sb.append(disSb.toString());
//				sb.append(":");
//				sb.append(speedSb.toString());
				sb.append("*");
				sb.append(roadclass);
			}
			return sb.toString();
		}
		return null;
	}

	private boolean judgeHasInOutLink(String links, String[] inoutlink) {
		// TODO Auto-generated method stub
		if (links.contains(inoutlink[0]) && links.contains(inoutlink[1])) {
			return true;
		} else {
			return false;
		}
	}
}
